package edu.udo.sopra10.chaturaji.ai;

import java.util.LinkedList;
import java.util.List;

import org.apache.commons.lang.NotImplementedException;

import edu.udo.sopra10.chaturaji.controllers.GameController;
import edu.udo.sopra10.chaturaji.entities.Player;
import edu.udo.sopra10.chaturaji.entities.coordinates.Position;
import edu.udo.sopra10.chaturaji.entities.pieces.Boat;
import edu.udo.sopra10.chaturaji.entities.pieces.Elephant;
import edu.udo.sopra10.chaturaji.entities.pieces.Horse;
import edu.udo.sopra10.chaturaji.entities.pieces.King;
import edu.udo.sopra10.chaturaji.entities.pieces.Pawn;
import edu.udo.sopra10.chaturaji.entities.pieces.Piece;
import edu.udo.sopra10.chaturaji.entities.transfer.DiceRoll;
import edu.udo.sopra10.chaturaji.entities.transfer.MoveRequest;
import edu.udo.sopra10.chaturaji.entities.transfer.TransferGame;
import edu.udo.sopra10.chaturaji.entities.transfer.DiceRoll.EquivalenceClass;
import edu.udo.sopra10.chaturaji.events.BoardResetEvent;
import edu.udo.sopra10.chaturaji.events.MoveRequestEvent;
import edu.udo.sopra10.chaturaji.events.TurnRequestEvent;
import edu.udo.sopra10.chaturaji.exceptions.NoPieceException;

public class MediumAI extends AbstractAI implements HintGivers {
	
	private class ReturnPieceAndPosition{
		private Position position;
		private Piece piece;
		
		ReturnPieceAndPosition(Piece piece, Position position) {
			this.position = position;
			this.piece = piece;
		}

		public Position getPosition() {
			return position;
		}

		public Piece getPiece() {
			return piece;
		}
	}

	public MediumAI() {
		GameController.addMoveRequestEventListener(this);
		GameController.addBoardResetEventListener(this);
		GameController.addMoveEventListener(this);
		GameController.addTurnRequestEventListener(this);
		GameController.setHintGiver( this );
	}

	/**
	 * @param piece piece that is to be moved.
	 * @param position target position.
	 * @return evaluation.
	 */
	protected int evaluatePosition(Piece piece, Position position) {
		int evaluation = 0;
		Position currentPosition = piece.getSquare().getPosition();
		
		Piece capturedPiece = board[position.getX()][position.getY()].getPiece();
		if ( capturedPiece != null && !capturedPiece.getSquare().getPosition().equals( piece.getSquare().getPosition() ) )
			evaluation += 2 * capturedPiece.getPieceValue();
		
		board[currentPosition.getX()][currentPosition.getY()].setPiece( null );
		evaluation += 5 * piece.getPossibleActions( board, position ).getPossibleMoves().size();
		evaluation += 7 * piece.getPossibleActions( board, position ).getPossibleCaptures().size();
		evaluation += 8 - Math.abs( 3 - position.getX() ) - Math.abs( 4 - position.getY() );
		board[currentPosition.getX()][currentPosition.getY()].setPiece( piece );

		for ( Piece capturingPiece : getCapturingPieces( position ) )
		{
			if ( capturingPiece.getClass() == King.class || capturingPiece.getClass() == Pawn.class || capturingPiece.getClass() == Elephant.class )
				evaluation -= 20;
			else
				evaluation -= 10;
		}
		
		return evaluation;
	}
	
	/**
	 * @param piece piece that is to be moved.
	 * @param position target position.
	 * @return evaluation.
	 */
	protected int evaluatePawnPosition(Piece piece, Position position) {
		int evaluation = 0;
		Position currentPosition = piece.getSquare().getPosition();
		
		Piece capturedPiece = board[position.getX()][position.getY()].getPiece();
		if ( capturedPiece != null && !capturedPiece.getSquare().getPosition().equals( piece.getSquare().getPosition() ) )
			evaluation += 2 * capturedPiece.getPieceValue();
		
		System.out.println("positionvalue pawn0" + evaluation);
		board[currentPosition.getX()][currentPosition.getY()].setPiece( null );
		evaluation += 5 * piece.getPossibleActions( board, position ).getPossibleMoves().size();
		evaluation += 7 * piece.getPossibleActions( board, position ).getPossibleCaptures().size();
		System.out.println("positionvalue pawn1" + evaluation);
		
		switch (piece.getOwner().getTurnOrderPosition() )
		{
			case 0:
//				System.out.println("figur player0 " + piece);
				switch (piece.getSquare().getPosition().getY() )
				{
					case 1:
//						System.out.println("figur player01 " + piece);
						evaluation += 26 + Math.abs( 2 * position.getX() );
//						System.out.println("positionvalue pawn2 " + evaluation);
						break;
					case 2:
//						System.out.println("figur player02 " + piece);
						evaluation += 30 + Math.abs( 2 * position.getX() );
//						System.out.println("positionvalue pawn2 " + evaluation);
						break;
					case 3:
//						System.out.println("figur player03 " + piece);
						evaluation += 28 + Math.abs( 2 * position.getX() );
//						System.out.println("positionvalue pawn2 " + evaluation);
						break;
					case 4:
//						System.out.println("figur player04 " + piece);
						evaluation += 28 + Math.abs( 2 * position.getX() );
//						System.out.println("positionvalue pawn2 " + evaluation);
						break;
					case 5:
//						System.out.println("figur player05 " + piece);
						evaluation += 30 + Math.abs( 2 * position.getX() );
						break;
					case 6:
//						System.out.println("figur player06 " + piece);
						evaluation += 26 + Math.abs( 5 * position.getX() );
						break;
					default:
//						System.out.println("figur player06 " + piece);
						evaluation += 25 + Math.abs( 5 * position.getX() );
						break;
					
					
				}

				
				
				break;
			case 1:
//				System.out.println("figur player1 " + piece);
				evaluation += 25 + Math.abs( 5 * position.getY() );
				break;
			case 2:
//				System.out.println("figur player2 " + piece);
				evaluation += 60 - Math.abs( 5 * position.getX() );
				break;
			case 3:
//				System.out.println("figur player3 " + piece);
				evaluation += 60 - Math.abs( 5 * position.getY() );
				break;
		}
//		System.out.println("positionvalue pawn4 " + evaluation);
		
		board[currentPosition.getX()][currentPosition.getY()].setPiece( piece );

		for ( Piece capturingPiece : getCapturingPieces( position ) )
		{
			if ( capturingPiece.getClass() == King.class || capturingPiece.getClass() == Pawn.class || capturingPiece.getClass() == Elephant.class )
				evaluation -= 20;
			else
				evaluation -= 10;
		}
		
		return evaluation;
	}

	/**
	 * @param pieces
	 * @return
	 */
	private ReturnPieceAndPosition getBestPositionKingPawn(LinkedList<Piece> pieces) {
		Position bestPosition = null;
		int bestValue = 0;
		Piece bestPiece = null;
//		System.out.println("Best position ");
		for (Piece piece : pieces) {

			// current position
			int positionValue = evaluatePosition(piece, piece.getSquare().getPosition());
//			System.out.println("positionvalue stand " + positionValue);
			if (positionValue >= bestValue) {
				bestPosition = piece.getSquare().getPosition();
				bestValue = positionValue;
				bestPiece = piece;
			}
			
			for (Position position : piece.getPossibleActions().getPossibleMoves()) {

//				System.out.println("figur21 " + getPiece( Pawn.class ));
				if(piece.getClass() != King.class){
					
//					System.out.println("figur3 " + piece);
					
					positionValue = evaluatePawnPosition(piece, position);
//					System.out.println("positionvalue " + positionValue);
				}
				else{
					positionValue = evaluatePosition(piece, position);
					System.out.println("figur4 " + piece);
//					System.out.println("positionvalue " + positionValue);
				}
				
				
				if (positionValue >= bestValue) {
					bestPosition = position;
					bestValue = positionValue;
					bestPiece = piece;
				}
			}
			
			for (Position position : piece.getPossibleActions().getPossibleCaptures()) {
				
				
				positionValue = evaluatePosition(piece, position);
				
				
				
				if (positionValue >= bestValue) {
					bestPosition = position;
					bestValue = positionValue;
					bestPiece = piece;
				}
			}
		}
		
		return new ReturnPieceAndPosition(bestPiece, bestPosition);
	}

	/**
	 * @param piece
	 * @return
	 */
	private Position getBestPosition(Piece piece) {
		Position bestPosition = piece.getSquare().getPosition();
		int bestValue = evaluatePosition( piece, bestPosition );
		int positionValue;
		
		for (Position position : piece.getPossibleActions().getPossibleMoves()) {
			positionValue = evaluatePosition(piece, position);
			if (positionValue >= bestValue ) {
				bestPosition = position;
				bestValue = positionValue;
			}
		}
		
		for (Position position : piece.getPossibleActions().getPossibleCaptures()) {
			positionValue = evaluatePosition(piece, position);
			if (positionValue >= bestValue) {
				bestPosition = position;
				bestValue = positionValue;
			}
		}
		
		return bestPosition;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * edu.udo.sopra10.chaturaji.ai.AbstractAI#getCaptureMove(edu.udo.sopra10
	 * .chaturaji.entities.pieces.Piece)
	 */
	@Override
	protected Position getCaptureMove(Piece piece) {
		throw new NotImplementedException();
	}

	protected MoveRequest getMove ( EquivalenceClass dice )
	{
		try
		{
			Piece piece = null;
			Position bestPosition = null;
			pieces = new LinkedList<Piece>( player.getPieces() );
			switch ( dice )
			{
				case ONE_OR_FIVE:
					LinkedList<Piece> availablePieces = getPieces();
					ReturnPieceAndPosition obj = getBestPositionKingPawn( availablePieces );
					piece = obj.getPiece();
					bestPosition = obj.getPosition();
					break;
				case TWO:
					piece = getPiece( Boat.class );
					bestPosition = getBestPosition( piece );
					break;
				case THREE:
					piece = getPiece( Horse.class );
					bestPosition = getBestPosition( piece );
					
					break;
				case FOUR_OR_SIX:
					piece = getPiece( Elephant.class );
					bestPosition = getBestPosition( piece );
					break;
			}
			if ( bestPosition == null || piece.getSquare().getPosition().equals( bestPosition ) )
			{
				return null;
			}
			
			return new MoveRequest( piece.getSquare().getPosition(), bestPosition );
		}
		catch ( NoPieceException e )
		{
			return null;
		}
	}
	
	@Override
	public void onMoveRequestEvent ( MoveRequestEvent event )
	{
		if ( event.getPlayerType() == Player.Type.AI_MEDIUM )
		{
			game = new TransferGame( GameController.getGame() );
			board = game.getBoard();
			player = game.getPlayers().get( event.getPlayerId() );
			
			try
			{
				MoveRequest request = null;
				while ( request == null )
					request = getMove( DiceRoll.equivalenceClassFromRoll( event.getUnassignedRolls().remove( 0 ) ) );
				
				GameController.move( request.start, request.end );
			}
			catch ( IndexOutOfBoundsException e )
			{
				GameController.pass();
			}
		}
	}

	@Override
	public void onTurnRequestEvent(TurnRequestEvent event) {
		if(event.getPlayerType() == Player.Type.AI_MEDIUM){
			player = game.getPlayers().get(event.getPlayerId());
			GameController.roll();
		}		
	}

	@Override
	public void onBoardResetEvent(BoardResetEvent event) {
		game = new TransferGame( GameController.getGame() );
		board = game.getBoard();		
	}

	@Override
	public MoveRequest getHint ( List<Byte> rolls, byte playerId )
	{
		game = new TransferGame( GameController.getGame() );
		board = game.getBoard();
		player = game.getPlayers().get( playerId );
		
		try
		{
			MoveRequest request = null;
			while ( request == null )
				request = getMove( DiceRoll.equivalenceClassFromRoll( rolls.remove( 0 ) ) );
			
			return request;
		}
		catch ( IndexOutOfBoundsException e )
		{
			return null;
		}
	}
}
